/**
  ******************************************************************************
  * @file    oled.c
  * @brief   This file provides code for the configuration
  *          of the oled instances.
  ******************************************************************************
  *  	0.96 寸 OLED 显示屏IIC接口DMA方式的 HAL库驱动程序
  *		Copyright (c) 202212 V1.0
	*		设   计 ：王 璠
  ******************************************************************************
  */
/*在整屏刷新时开启第一次DMA发送cmd函数，进入完成传输后进入完成回调函数，
发送第二次的数据，待发送完成后进入下次的回调函数 ~~~~直至完成所有的传输。*/
	
	
#include "oled.h"
#include "oledfont.h"
#include "stdlib.h"
//#include "chinese.h"

#include "stm32f4xx.h"
#include "stm32f4xx_hal_gpio.h"
//#include "main.h"


//几个变量声明



//uint8_t OLED_GRAM[128][8];
uint8_t OLED_GRAMbuf[8][128];//建立一个缓冲Buffer存储整个屏幕需要现实的信息。
uint8_t OLED_CMDbuf[8][4] = {0};//cmd地址Buffer,存储地址信息。
uint8_t I2C1_MemTxFinshFlag = 1;
uint8_t CountFlag = 0; 
uint8_t BufFinshFlag = 0; 











uint8_t OLED_GRAM[144][8];//显示存储器
uint8_t **Hzk;

//-------------------------------------------------------------------------------------------------
//					IIC以DMA方式向某个地址的从设备发送一定长度的数据
//										发送完成中断回调函数
// HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData,uint16_t Size)
//					完成回调函数,保证DMA传输完成后，开启下次DMA
// 
//-------------------------------------------------------------------------------------------------	
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
		if(BufFinshFlag)
	{
		HAL_I2C_Mem_Write_DMA(&hi2c1,0x78,0x40,I2C_MEMADD_SIZE_8BIT,OLED_GRAMbuf[CountFlag],128);
			
		HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_15);	//调试测试中断频率显示，主板LED闪烁频率
//		printf("CountFlag_g = %d  ",CountFlag);
	}
}


//----------------------------------------------------------------------------------------------------
//		IIC以DMA方式向从设备的指定存储地址传送一定长度的数据
//								完成中断回调函数
//  HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress,
//     uint16_t MemAddSize, uint8_t *pData, uint16_t Size)完成回调函数
// 					保证DMA传输完成后，开启下次DMA
//---------以-------------------------------------------------------------------------------------------
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	if(CountFlag == 7)
	{
		BufFinshFlag = 0;
		CountFlag = 0;
	}
	if(BufFinshFlag)
	{
		CountFlag ++;
		HAL_I2C_Master_Transmit_DMA(&hi2c1,0x78,OLED_CMDbuf[CountFlag],4);
			HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_15);	
		
//		printf("CountFlag_cmd = %d  ",CountFlag);
	}
}
//----------------------------------------------------------------------------------------------------
// 				初始化OLED模块命令，
// 输入参数   none
// 输出参数   none
//----------------------------------------------------------------------------------------------------
//uint8_t OLED_Init_CMD[ ] =			//0xD3, 0x00,正常显示OLED
//{
//	0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1, 0xA6, 0xA8,
//	0x3F, 0xC8, 0xD3, 0x00, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1,
//	0xDA, 0x12, 0xDB, 0x30, 0x8D, 0x14, 0xAF, 0x20, 0x00
//};
uint8_t OLED_Init_CMD[ ] =			//0xD3, 0xf8,行错位则修改OLED参数
{
	0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1, 0xA6, 0xA8,
	0x3F, 0xC8, 0xD3, 0xf8, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1,
	0xDA, 0x12, 0xDB, 0x30, 0x8D, 0x14, 0xAF, 0x20, 0x00
};
//----------------------------------------------------------------------- -----------------------------

//-----------------------------------------------------------------------------
//2022-12-19 王璠注：
//《基于STM32F407的 中景园0.96寸OLED（IIC）的程序升级（DMA+IIC + 显存Buffer）》)
//若发现行错位，则修改

//	WriteCmd(0xd3); //-set display offset
//	WriteCmd(0x00 加或减 8的倍数); 
//-----------------------------------------------------------------------------	
void OLED_init(void)
{
	HAL_I2C_Mem_Write_DMA(&hi2c1, OLED_I2C_ADDRESS, 0x00, I2C_MEMADD_SIZE_8BIT, OLED_Init_CMD, 29);
}


//----------------------------------------------------------------------------------------------------
//         发送数据到OLED的GRAM
// 输入参数     OLED_GRAM[128][8]
// 输出参数     OLED_GRAMbuf[8][128]
//----------------------------------------------------------------------------------------------------
void OLED_refresh_gram(void)
{	
	uint8_t i;
	uint16_t j;
		
	if(BufFinshFlag == 0)
	{
		for(i = 0; i < 8; i ++ )
		{
//			OLED_set_pos(0, i);
			for(j = 0;j < 128; j ++)
			{
					OLED_GRAMbuf[i][j] = OLED_GRAM[j][i];  //OLED_GRAM[128][8]
			}
		}
		
//新LCD	的开始地址错位8位的一行		
		
		BufFinshFlag = 1;
		HAL_I2C_Master_Transmit_DMA(&hi2c1,0x78,OLED_CMDbuf[0],4);
	}
}

//----------------------------------------------------------------------------------------------------
//         发送数据到OLED的GRAM
// 输入参数     OLED_GRAM[128][8]
// 输出参数     OLED_GRAMbuf[8][128]
//----------------------------------------------------------------------------------------------------
void NEWOLED_refresh_gram(void)
{	
	uint8_t i;
	uint16_t j;
		
	if(BufFinshFlag == 0)
	{
		for(i = 0; i < 8; i ++ )
		{
//			OLED_set_pos(0, i);
			for(j = 0;j < 128; j ++)
			{
					OLED_GRAMbuf[i][j] = OLED_GRAM[j][i];  //OLED_GRAM[128][8]
			}
		}
		
		
		for(i = 0; i < 8; i ++ )
		{
			for(j = 0;j < 128; j ++)
			{
					OLED_GRAMbuf[i+1][j] = OLED_GRAMbuf[i][j];  //OLED_GRAM[128][8]
			}
		}
		{

//			for(j = 0;j < 128; j ++)
//			{
//					OLED_GRAMbuf[7][j] = OLED_GRAM[j][0];  //OLED_GRAM[128][8]
//			}
		}
		
//新LCD	的开始地址错位8位的一行		
		
		BufFinshFlag = 1;
		HAL_I2C_Master_Transmit_DMA(&hi2c1,0x78,OLED_CMDbuf[0],4);
	}
}


//---------------------------------------------------------------
//OLED屏初始化命令共27条
//---------------------------------------------------------------
uint8_t CMD_Data[]={
0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1, 0xA6, 0xA8, 0x3F,					
0xC8, 0xD3, 0x00, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1, 0xDA, 0x12,					
0xD8, 0x30, 0x8D, 0x14, 0xAF};

/*
(0xAE,OLED_CMD);//--turn off oled panel
(0x00,OLED_CMD);//---set low column address
(0x10,OLED_CMD);//---set high column address
(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
(0x81,OLED_CMD);//--set contrast control register
(0xCF,OLED_CMD);// Set SEG Output Current Brightness
(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
(0xA6,OLED_CMD);//--set normal display
(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)

(0x3f,OLED_CMD);//--1/64 duty
(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
(0x00,OLED_CMD);//-not offset
(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
(0xD9,OLED_CMD);//--set pre-charge period
(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
(0xDA,OLED_CMD);//--set com pins hardware configuration
(0x12,OLED_CMD);
(0xDB,OLED_CMD);//--set vcomh

(0x30,OLED_CMD);//Set VCOM Deselect Level
(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
(0x02,OLED_CMD);//
(0x8D,OLED_CMD);//--set Charge Pump enable/disable
(0x14,OLED_CMD);//--set(0x10) disable
	*/








//---------------------------------------------------------------
//初始化命令
//---------------------------------------------------------------
void WriteCmd()
{
	uint8_t i = 0;
	for(i=0; i<27; i++)
	{
		HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,CMD_Data+i,1,0x100);
	}
}
//---------------------------------------------------------------
//向设备写入控制命令
//---------------------------------------------------------------
void OLED_WR_CMD(uint8_t cmd)
{
	HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x00,I2C_MEMADD_SIZE_8BIT,&cmd,1,0x100);
}
//---------------------------------------------------------------
//向设备写入数据
//---------------------------------------------------------------
void OLED_WR_DATA(uint8_t data)
{
	HAL_I2C_Mem_Write(&hi2c1 ,0x78,0x40,I2C_MEMADD_SIZE_8BIT,&data,1,0x100);
}
//---------------------------------------------------------------
//初始化oled屏幕
//---------------------------------------------------------------
void OLED_Init(void)
{ 	
	HAL_Delay(200);
	WriteCmd();
}










//---------------------------------------------------------------
//清屏size12 size16要清两行，其他函数有类似情况
//c_16f=0,2,4,6
//c_16e:=2,4,6,8
//c_16f<c_16e
//---------------------------------------------------------------
void OLED_Clear(uint8_t c_16f,uint8_t c_16e)
{
	uint8_t i,n;		    
	for(i=c_16f;i<c_16e;i++)  
		{  
				for(n=0;n<128;n++)
				{
					OLED_GRAM[n][i]=0;//清除显示缓冲区所有数据
				}
		}
	
//		OLED_refresh_gram();
//		OLED_Refresh();//更新显示
}
//---------------------------------------------------------------
//清行
//---------------------------------------------------------------
void OLED_Clearrow(uint8_t i)
{
	uint8_t n;
		OLED_WR_CMD(0xb0+i);
		OLED_WR_CMD (0x00); 
		OLED_WR_CMD (0x10); 
		for(n=0;n<128;n++)
			{
					OLED_WR_DATA(0);
			}
}
//---------------------------------------------------------------
//开启OLED显示  
//---------------------------------------------------------------
void OLED_Display_On(void)
{
	OLED_WR_CMD(0X8D);  //SET DCDC命令//电荷泵使能
	OLED_WR_CMD(0X14);  //DCDC ON//开启电荷泵
	OLED_WR_CMD(0XAF);  //DISPLAY ON//点亮屏幕
}
//---------------------------------------------------------------
//关闭OLED显示  
//---------------------------------------------------------------
void OLED_Display_Off(void)
{
	OLED_WR_CMD(0X8D);  //SET DCDC命令
	OLED_WR_CMD(0X10);  //DCDC OFF
	OLED_WR_CMD(0XAE);  //DISPLAY OFF
}	
//---------------------------------------------------------------
//设置显示位置坐标
//---------------------------------------------------------------
void OLED_Set_Pos(uint8_t x, uint8_t y) 
{ 	
	OLED_WR_CMD(0xb0+y);
	OLED_WR_CMD(((x&0xf0)>>4)|0x10);
	OLED_WR_CMD(x&0x0f);
} 
//---------------------------------------------------------------

//---------------------------------------------------------------
void OLED_On(void)  
{  
	uint8_t i,n;		    
	for(i=0;i<8;i++)  
	{  
		OLED_WR_CMD(0xb0+i);    //设置页地址（0~7）
		OLED_WR_CMD(0x00);      //设置显示位置—列低地址
		OLED_WR_CMD(0x10);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)
			{
				OLED_WR_DATA(1); 
			}
	} //更新显示
}
//---------------------------------------------------------------
//反显函数
//---------------------------------------------------------------
void OLED_ColorTurn(uint8_t i)
{
	if(i==0)
		{
			OLED_WR_CMD(0xA6);//正常显示
		}
	if(i==1)
		{
			OLED_WR_CMD(0xA7);//反色显示
		}
}
//---------------------------------------------------------------
//屏幕旋转180度
//---------------------------------------------------------------
void OLED_DisplayTurn(uint8_t i)
{
	if(i==0)
		{
			OLED_WR_CMD(0xC8);//正常显示
			OLED_WR_CMD(0xA1);
		}
	if(i==1)
		{
			OLED_WR_CMD(0xC0);//反转显示
			OLED_WR_CMD(0xA0);
		}
}
//---------------------------------------------------------------
//更新显存到OLED
//---------------------------------------------------------------
void OLED_Refresh(void)
{
	uint8_t i,n;
	for(i=0;i<8;i++)
		{
				OLED_WR_CMD(0xb0+i); //设置行起始地址
				OLED_WR_CMD(0x00);   //设置低列起始地址
				OLED_WR_CMD(0x10);   //设置高列起始地址
			for(n=0;n<128;n++)
					{
						OLED_WR_DATA(OLED_GRAM[n][i]);//将显存数据写入到OLED中
					}
		}
}
//---------------------------------------------------------------
//画点 
//x:	0~127
//y:	0~63
//t:	1-->填充 ；0-->清空	
//---------------------------------------------------------------
void OLED_DrawPoint(uint8_t x,uint8_t y,uint8_t t)
{
	uint8_t i,m,n;
		i=y/8;
		m=y%8;
		n=1<<m;
	if(t)		//1-->填充
		{
		OLED_GRAM[x][i]|=n;
		}
	else	//0-->清空	
		{
			OLED_GRAM[x][i]=~OLED_GRAM[x][i];
			OLED_GRAM[x][i]|=n;
			OLED_GRAM[x][i]=~OLED_GRAM[x][i];
		}
}
//---------------------------------------------------------------







//---------------------------------------------------------------
//功能：	在指定位置显示一个字符（写入显存）,包括部分字符
// 		函数参数设置：
//1:		x:0~127
//2:		y:0~63
//3：		chr
//4:		size1:	选择字体 8-->6x8
//											12-->6x12
//											16-->8x16
//											24-->12x24
//5:		mode:	0,反色显示;1,正常显示
//例如：OLED_ShowChar(x,y,chr[j],Char_Size,1);
//---------------------------------------------------------------
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size1,uint8_t mode)
{
	uint8_t i,m,temp,size2,chr1;
	uint8_t x0=x,y0=y;
	
	if(size1==8)
		{	
		size2=6;
		}
	else 
		{
			size2=(size1/8+((size1%8)?1:0))*(size1/2);  
			//得到字体一个字符对应点阵集所占的字节数
		}
		chr1=chr-' ';  //计算偏移后的值
		
	for(i=0;i<size2;i++)
		{
			if(size1==8)
					{
					temp=asc2_0806[chr1][i];
					} //调用0806字体
			else if(size1==12)
					{
					temp=asc2_1206[chr1][i];
					} //调用1206字体
			else if(size1==16)
					{
					temp=asc2_1608[chr1][i];
					} //调用1608字体
			else if(size1==24)
					{
					temp=asc2_2412[chr1][i];
					} //调用2412字体
			else return;
			
			for(m=0;m<8;m++)		
				{
					if(temp&0x01)		//字体一个字符对应点阵集写入显存
						{
							OLED_DrawPoint(x,y,mode);
						}
					else 
						{
							OLED_DrawPoint(x,y,!mode);
						}
					temp>>=1;
					y++;
				}
			
				x++;
			if((size1!=8)&&((x-x0)==size1/2))
				{
					x=x0;y0=y0+8;
				}
			y=y0;
		}
}
//---------------------------------------------------------------
void oled_ccdv(void)
{
int i=0;	
uint8_t x,y;	
//		OLED_ShowString(0,16,"7654321001234567",16);	
		
		OLED_Clear(4,8);
				
					for(i=0;i<127;i++)
					{
						x=i;
					y=63-aucImageBuf[i]/8;
					OLED_DrawPoint(x,y,1);//画点坐标 x:	0~127 y:	0~63
					}
}

//---------------------------------------------------------------
unsigned int oled_pow(uint8_t m,uint8_t n)
{
	unsigned int result=1;	 
	while(n--)result*=m;    
	return result;
}


//---------------------------------------------------------------
//					显示2个数字
//---------------------------------------------------------------
//1:		x起点坐标:	0~127
//2:		y起点坐标:	0~63	
//3：		num:	数值(0~4294967295)
//4：		len :	数值num的位数
//5：		size:	字体大小 size2=8，12,16,24
//例如：OLED_ShowNum(24,48,display_buf_8led,4,16);	//显示ad采样值	
//---------------------------------------------------------------
void OLED_ShowNum(uint8_t x,uint8_t y,unsigned int num,uint8_t len,uint8_t size2)
{         	
	uint8_t t,temp;
	uint8_t enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/oled_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				OLED_ShowChar(x+(size2/2)*t,y,' ',size2,1);
				continue;
			}else enshow=1; 
		 	 
		}
	 	OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2,1); 
	}
}
//---------------------------------------------------------------
//				显示一个字符号串
//---------------------------------------------------------------
//1:		x起点坐标:	0~127
//2:		y起点坐标:	0~63	 
//3：		*chr:	字符串
//4：		size:	字体大小 size2=8,12,16,24
//---------------------------------------------------------------
void OLED_ShowString(uint8_t x,uint8_t y,unsigned char *chr,uint8_t Char_Size)
{
	unsigned char j=0;
	while (chr[j]!='\0')
	{		OLED_ShowChar(x,y,chr[j],Char_Size,1);
			x+=8;
		if(x>120){x=0;y+=2;}
			j++;
	}
}
//---------------------------------------------------------------
//显示汉字
//hzk 用取模软件得出的数组
//---------------------------------------------------------------
/*void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no)
{      			    
	uint8_t t,adder=0;
	OLED_Set_Pos(x,y);	
    for(t=0;t<16;t++)
		{
				OLED_WR_DATA(Hzk[2*no][t]);
				adder+=1;
     }	
		OLED_Set_Pos(x,y+1);	
    for(t=0;t<16;t++)
			{	
				OLED_WR_DATA(Hzk[2*no+1][t]);
				adder+=1;
      }					
}*/
//---------------------------------------------------------------
//								显示汉字
//---------------------------------------------------------------
//1:		x起点坐标:	0~127
//2:		y起点坐标:	0~63	 
//3：		num:	汉字在字形数据数组对应的序号
//4			size:	字体大小 size2=16   暂时没有用到这个参数，固定为16
//5：		mode:	0,反色显示;1,正常显示
//6:		src[]:	汉字字体字形数据数组
//	例如：OLED_ShowChinese(8+i*18,0,i,16,1,college[i]);
//---------------------------------------------------------------
void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t num,uint8_t size1,uint8_t mode,uint8_t src[])
{
	uint8_t m,temp;
	uint8_t x0=x,y0=y;
	uint32_t i;  
	for(i=0;i<32;i++)
	{
		temp=src[i];//调用字体

		for(m=0;m<8;m++)
			{
				if(temp&0x01)
					{
						OLED_DrawPoint(x,y,mode);
					}
				else 
					{
						OLED_DrawPoint(x,y,!mode);
					}
				temp>>=1;
				y++;
			}
		x++;
		if((x-x0)==16)
			{
					x=x0;
					y0=y0+8;
			}
		y=y0;
	}
}
//---------------------------------------------------------------



